import requests
import execjs
import os
import re
import logging
from tqdm import tqdm

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# 全局变量
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0",
    "Referer": "https://www.bilibili.com/",
    "Cookie": "b_nut=1758193986; buvid3=32A5A91E-C687-0E53-0062-D130C66C1E6686495infoc"
}

def get_w_rids(t):
    try:
        with open("code.js", "r", encoding="utf-8") as f:
            js_code = f.read()
        ctx = execjs.compile(js_code)
        return ctx.call("Getw_rids", t)
    except FileNotFoundError:
        logging.error("code.js 文件不存在，请检查路径。")
    except execjs.Error as e:
        logging.error(f"执行JS代码出错: {e}")
    return None

def get_wbi_params():
    nav_url = "https://api.bilibili.com/x/web-interface/nav"
    try:
        response = requests.get(nav_url, headers=headers, timeout=10)
        response.raise_for_status()
        data = response.json().get('data', {}).get('wbi_img', {})
        if not data:
            raise ValueError("获取wbi_img数据失败")
        img_url = data.get('img_url', '')
        sub_url = data.get('sub_url', '')
        imgKey = img_url.split('/')[-1].split('.')[0]
        subKey = sub_url.split('/')[-1].split('.')[0]
        logging.info(f"imgKey: {imgKey}")
        logging.info(f"subKey: {subKey}")
        return {"wbiImgKey": imgKey, "wbiSubKey": subKey}
    except requests.exceptions.RequestException as e:
        logging.error(f"请求nav API失败: {e}")
    except (ValueError, KeyError) as e:
        logging.error(f"解析wbi参数失败: {e}")
    return None

def get_bvid(url):
    match = re.search(r'(BV\w{10})', url)
    return match.group(1) if match else None

def get_title(bvid):
    api_url = f"https://www.bilibili.com/video/{bvid}"
    try:
        response = requests.get(api_url, headers=headers, timeout=10)
        response.raise_for_status()
        match = re.search(r'<title>(.*?)</title>', response.text)
        if match:
            title = match.group(1).strip()
            return re.sub(r'[\\/:*?"<>|]', '', title)
    except requests.exceptions.RequestException as e:
        logging.error(f"获取视频标题失败: {e}")
    return "未知标题"

def get_cid(bvid):
    api_url = f"https://api.bilibili.com/x/player/pagelist?bvid={bvid}&jsonp=jsonp"
    try:
        response = requests.get(api_url, headers=headers, timeout=10)
        response.raise_for_status()
        data = response.json()
        if data.get('code') == 0 and data.get('data'):
            cid = data['data'][0]['cid']
            logging.info(f"cid: {cid}")
            return {"cid": cid}
    except requests.exceptions.RequestException as e:
        logging.error(f"获取CID失败: {e}")
    return None

def get_download_video(bvid, cid, wts, w_rid):
    if isinstance(cid, dict):
        cid_value = cid.get("cid")
    else:
        cid_value = cid
    if not w_rid or not cid_value:
        logging.error("缺少 w_rid 或 cid 参数。")
        return None
    try:
        api_url = f"https://api.bilibili.com/x/player/wbi/playurl?bvid={bvid}&cid={cid_value}&fnval=4048&try_look=1&wts={wts}&w_rid={w_rid}"
        response = requests.get(api_url, headers=headers, timeout=10)
        response.raise_for_status()
        data = response.json()
        # print(data)
        if data.get('code') == 0:
            video_list = data.get('data', {}).get('dash', {}).get('video', [])
            audio_list = data.get('data', {}).get('dash', {}).get('audio', [])
            video_url = video_list[0].get('baseUrl') if video_list else None
            audio_url = audio_list[0].get('baseUrl') if audio_list else None
            logging.info(f"视频下载链接: {video_url}")
            logging.info(f"音频下载链接: {audio_url}")
            if video_url and audio_url:
                return {"video_url": video_url, "audio_url": audio_url}
    except requests.exceptions.RequestException as e:
        logging.error(f"获取下载链接失败: {e}")
    return None

# def _download_stream(url, filename, desc):
#     try:
#         response = requests.get(url, headers=headers, stream=True, timeout=30)
#         response.raise_for_status()
#         total_size = int(response.headers.get('content-length', 0))
#         with open(filename, 'wb') as f, tqdm(desc=desc, total=total_size, unit='B', unit_scale=True, unit_divisor=1024) as bar:
#             for chunk in response.iter_content(chunk_size=8192):
#                 f.write(chunk)
#                 bar.update(len(chunk))
#         return True
#     except requests.exceptions.RequestException as e:
#         logging.error(f"下载失败: {e}")
#     return False

# def download_file(video_url, audio_url, filename):
#     video_temp_file = "temp_video.m4s"
#     audio_temp_file = "temp_audio.m4s"
#     headers["Referer"] = f"https://www.bilibili.com/video/{bvid}"
#     logging.info("开始下载视频流...")
#     video_downloaded = _download_stream(video_url, video_temp_file, "视频流")
#     logging.info("开始下载音频流...")
#     audio_downloaded = _download_stream(audio_url, audio_temp_file, "音频流")
#     if video_downloaded and audio_downloaded:
#         logging.info("开始合并音视频流...")
#         os.system(f'ffmpeg -y -v quiet -i "{video_temp_file}" -i "{audio_temp_file}" -c copy "{filename}.mp4"')
#         logging.info(f"合并完成，文件已保存为 {filename}.mp4")
#     else:
#         logging.error("下载失败，跳过合并。")
#     for temp_file in [video_temp_file, audio_temp_file]:
#         if os.path.exists(temp_file):
#             os.remove(temp_file)
#             logging.info(f"临时文件 {temp_file} 已删除。")

if __name__ == "__main__":
    save_path = "B站视频"
    os.makedirs(save_path, exist_ok=True)
    bilibili_video_url = input("请输入B站视频链接：")
    bvid = get_bvid(bilibili_video_url)
    headers["Referer"] = f"https://www.bilibili.com/video/{bvid}"
    if not bvid:
        logging.error("无效的B站视频链接。")
    else:
        title = get_title(bvid)
        logging.info(f"title: {title}")
        filename = os.path.join(save_path, title)
        cid_info = get_cid(bvid)
        if cid_info:
            cid = cid_info["cid"]
            wbi_params = get_wbi_params()
            if wbi_params:
                wbi_param = {"wbiImgKey": wbi_params["wbiImgKey"], "wbiSubKey": wbi_params["wbiSubKey"]}
                w_rid_list = get_w_rids(wbi_param)
                if w_rid_list:
                    w_rid = w_rid_list["w_rid"]
                    wts = w_rid_list["wts"]
                    logging.info(f"w_rid: {w_rid}, wts: {wts}")
                    download_info = get_download_video(bvid, cid, wts, w_rid)
                    if download_info and download_info["video_url"] and download_info["audio_url"]:
                        video_url = download_info["video_url"]
                        audio_url = download_info["audio_url"]
                        # download_file(video_url, audio_url, filename)
                    else:
                        logging.error("未能获取到有效的视频或音频下载链接。")
                else:
                    logging.error("Wbi 签名生成失败。")
            else:
                logging.error("未能获取wbi参数。")
        else:
            logging.error("未能获取到视频的CID信息。")